﻿using System;
using System.Collections.Generic;
using System.Linq;
using BCCommon;
using BCCommon.Extensions;
using BCCommon.OAEnums;
using BCEntity.OA.Attendance.AttendanceCheckInRecord;
using BCEntity.OA.Attendance.AttendanceCheckInFlow;
using BCData.OA.Attendance.AttendanceCheckInFlow;
using BCData.OA.Attendance.AttendanceCheckInRecord;
using BCData.OA.Attendance.AttendanceGroup;
using BCDto.MySql.Admin;
using BCDto.OA.Attendance.AttendanceCheckInFlow;
using BCDto.OA.Attendance.AttendanceCheckInRecord;
using BCDto.OA.Attendance.AttendanceGroup;
using BCService.OA.Attendance.AttendanceGroup;
using BCService.Utils;

namespace BCService.OA.Attendance.AttendanceCheckInRecord
{
    /// <summary>
    /// 
    /// </summary>
    public class AttendanceCheckInRecordService : IAttendanceCheckInRecordService
    {
        private readonly IAttendanceCheckInRecordData checkInRecordData;
        private readonly IAttendanceGroupData groupData;
        private readonly IAttendanceGroupService groupService;
        private readonly IAttendanceCheckInFlowData checkInFlowData;

        public AttendanceCheckInRecordService(IAttendanceCheckInRecordData checkInRecordData, IAttendanceGroupData groupData, IAttendanceGroupService groupService, IAttendanceCheckInFlowData checkInFlowData)
        {
            this.checkInRecordData = checkInRecordData;
            this.groupData = groupData;
            this.groupService = groupService;
            this.checkInFlowData = checkInFlowData;
        }

        /// <summary>
        /// 新创建一条打卡记录信息
        /// </summary>
        /// <param name="admin">打卡员工信息</param>
        /// <param name="dto">打卡信息</param>
        /// <returns></returns>
        public AttendanceCheckInRecordDto CreateAttendanceCheckInRecord(AdminDto admin, AttendanceCheckInRecordRequestDto dto)
        {
            var result = new AttendanceCheckInRecordDto();
            var entity = dto.As<AttendanceCheckInRecordEntity>();
            var groupId = groupData.GetGroupIdByDepartmentIdAndCompanyIdAsync(admin.DepartmentId.Value, admin.CompanyId).GetAwaiter().GetResult();
            if (groupId <= 0)
            {
                //获取自由考勤组
                groupId = groupData.GetFirstNoneAttendanceGroup().GetAwaiter().GetResult().GroupId;
                dto.BaseCheckInTime = DateTime.Now.ToString("HH:00");
                if (groupId <= 0)
                {
                    throw new ArgumentException("未设置考勤组，请联系管理员。");
                }
            }
            if (dto.BaseCheckInTime.IsNull())
            {
                throw new ArgumentException("基准时间不能为空。");
            }
            var groupView = groupService.GetByGroupIdForStaffApp(groupId);
            entity.AttendanceGroupId = groupId;
            entity.DepartmentId = admin.DepartmentId.Value;
            entity.AdminId = admin.AdminId;
            entity.AdminName = admin.FullName;
            entity.CompanyId = admin.CompanyId;
            entity.CheckInTime = DateTime.Now;
            //考勤组规则校验
            switch (groupView.GroupInfo.Type.As<AttendanceType>())
            {
                case AttendanceType.FIXED:
                    entity.ClassId = dto.SelectedClassId;
                    break;
                case AttendanceType.NONE:
                    if (DateTime.Now < Convert.ToDateTime(groupView.GroupInfo.StartTime))
                    {
                        throw new ArgumentException("还未到打卡时间。");
                    }
                    break;
                case AttendanceType.TURN:
                    if (dto.SelectedClassId <= 0)
                    {
                        throw new ArgumentException("请选择班次打卡。");
                    }
                    entity.ClassId = dto.SelectedClassId;
                    var baseTime = groupView.WorkDayList.First(s => s.ClassId == dto.SelectedClassId).Sections.First().start;
                    if (baseTime != dto.BaseCheckInTime)
                    {
                        dto.BaseCheckInTime = baseTime;
                    }
                    break;
                default:
                    entity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : CheckInStatus.Normal.ToString();
                    break;
            }
            switch (dto.CheckInType)
            {
                #region 上班
                case CheckInType.OnDuty:
                    var startRecord = checkInRecordData.GetIsExistsByTime(admin.AdminId, groupId, dto.SelectedClassId, dto.BaseCheckInTime).GetAwaiter().GetResult();
                    if (startRecord != null)
                    {
                        //更新记录
                        result = this.UpdateAttendanceCheckInRecord(admin, startRecord.CheckInRecordId, dto).As<AttendanceCheckInRecordDto>();
                    }
                    else
                    {
                        //上班打卡，判断时间是否迟到
                        DateTime onDutyTime = Convert.ToDateTime(dto.BaseCheckInTime);
                        //校验打卡方式           
                        if (dto.CheckInMode == CheckInMode.Wifi)
                        {
                            //优先查询到Wifi就直接可以进入下一阶段
                            if (groupView.Modes.WifiList.Any(s => s.MAC.ToUpper() != dto.MAC.ToUpper()))
                            {
                                entity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                            }
                            else
                            {
                                entity.Status = onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                            }
                        }
                        else
                        {
                            entity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                        }
                        result = checkInRecordData.InserAsync(entity).GetAwaiter().GetResult().As<AttendanceCheckInRecordDto>();
                    }
                    break;

                #endregion

                #region 下班

                case CheckInType.OffDuty:
                    var baseEndTime = groupView.WorkDayList.First(s => s.ClassId == dto.SelectedClassId).Sections.First().end;
                    if (baseEndTime != dto.BaseCheckInTime)
                    {
                        dto.BaseCheckInTime = baseEndTime;
                    }
                    var endRecord = checkInRecordData.GetIsExistsByTime(admin.AdminId, groupId, dto.SelectedClassId, dto.BaseCheckInTime).GetAwaiter().GetResult();
                    if (endRecord != null)
                    {
                        //更新记录
                        result = this.UpdateAttendanceCheckInRecord(admin, endRecord.CheckInRecordId, dto);
                    }
                    else
                    {
                        //下班打卡，判断时间是否是早退
                        DateTime offDutyTime = Convert.ToDateTime(dto.BaseCheckInTime);
                        //校验打卡方式           
                        if (dto.CheckInMode == CheckInMode.Wifi)
                        {
                            //优先查询到Wifi就直接可以进入下一阶段
                            if (!groupView.Modes.WifiList.Any(s => s.MAC.ToUpper().Trim() == dto.MAC.ToUpper().Trim()))
                            {
                                entity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                            }
                            else
                            {
                                entity.Status = offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                            }
                        }
                        else
                        {
                            entity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                        }
                        result = checkInRecordData.InserAsync(entity).GetAwaiter().GetResult().As<AttendanceCheckInRecordDto>();
                    }
                    break;
                #endregion
                default:
                    break;
            }

            return result;
        }

        /// <summary>
        /// 更新指定的打卡记录
        /// </summary>
        /// <param name="admin">管理员</param>
        /// <param name="checkInRecordId">打卡记录Id</param>
        /// <param name="dto">打卡信息</param>
        /// <returns></returns>
        public AttendanceCheckInRecordDto UpdateAttendanceCheckInRecord(AdminDto admin, long checkInRecordId, AttendanceCheckInRecordRequestDto dto)
        {
            var updateEntity = checkInRecordData.GetAsync(checkInRecordId).GetAwaiter().GetResult();
            if (updateEntity == null)
            {
                throw new ArgumentException("打卡信息不存在。");
            }
            updateEntity.ModifierId = admin.AdminId;
            updateEntity.ModifierName = admin.FullName;
            updateEntity.Latitude = dto.Latitude;
            updateEntity.Location = dto.Location;
            updateEntity.Longitude = dto.Longitude;
            updateEntity.WifiName = dto.WifiName;
            updateEntity.MAC = dto.MAC;
            AttendanceCheckInFlowEntity entity = new AttendanceCheckInFlowEntity
            {
                CheckInRecordId = checkInRecordId,
                Latitude = dto.Latitude,
                Location = dto.Location,
                Longitude = dto.Longitude,
                WifiName = dto.WifiName,
                MAC = dto.MAC,
                BaseCheckInTime = updateEntity.BaseCheckInTime,
                AdminId = admin.AdminId,
                AdminName = admin.FullName
            };
            var groupView = groupService.GetByGroupIdForStaffApp(updateEntity.AttendanceGroupId);
            switch (updateEntity.CheckInType.As<CheckInType>())
            {
                case CheckInType.OnDuty:
                    DateTime onDutyTime = Convert.ToDateTime(entity.BaseCheckInTime);
                    if (onDutyTime < DateTime.Now)
                    {
                        entity.Remark = "打卡无效，上班时间未到";
                    }
                    else
                    {
                        entity.Remark = "已经打过卡了,上班时间以最早打卡时间为准，本次仅记录打卡流水";
                    }
                    //校验打卡           
                    if (dto.CheckInMode == CheckInMode.Wifi)
                    {
                        //优先查询到Wifi就直接可以进入下一阶段
                        if (groupView.Modes.WifiList.Any(s => s.MAC.ToUpper() != dto.MAC.ToUpper()))
                        {
                            updateEntity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                        }
                        updateEntity.Status = onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                    }
                    else
                    {
                        updateEntity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : onDutyTime < DateTime.Now ? CheckInStatus.Late.ToString() : CheckInStatus.Normal.ToString();
                    }
                    checkInFlowData.InserAsync(entity).GetAwaiter().GetResult();
                    return checkInRecordData.UpdateAsync(updateEntity).GetAwaiter().GetResult().As<AttendanceCheckInRecordDto>();
                case CheckInType.OffDuty:
                    //校验下班打卡什么时候可以打
                    if (groupView.GroupInfo.LaterMinutes > 0 && (DateTime.Now - updateEntity.CheckInTime).Minutes < groupView.GroupInfo.LaterMinutes)
                    {
                        throw new ArgumentException($"上班后{groupView.GroupInfo.LaterMinutes}分钟才可打下班卡。");
                    }
                    DateTime onOffTime = Convert.ToDateTime(entity.BaseCheckInTime);
                    if (DateTime.Now < onOffTime)
                    {
                        entity.Remark = "打卡无效，下班时间未到";
                    }
                    else
                    {
                        entity.Remark = "打卡无效，此记录已被更新";
                    }
                    //更新打卡记录时间
                    DateTime offDutyTime = Convert.ToDateTime(updateEntity.BaseCheckInTime);
                    //校验打卡           
                    if (dto.CheckInMode == CheckInMode.Wifi)
                    {
                        //优先查询到Wifi就直接可以进入下一阶段
                        if (groupView.Modes.WifiList.Any(s => s.MAC.ToUpper() != dto.MAC.ToUpper()))
                        {
                            updateEntity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                        }
                        updateEntity.Status = offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                    }
                    else
                    {
                        updateEntity.Status = this.IsField(groupView, dto.CheckInMode, dto.Longitude, dto.Latitude) ? CheckInStatus.Field.ToString() : offDutyTime > DateTime.Now ? CheckInStatus.LeaveEarly.ToString() : CheckInStatus.Normal.ToString();
                    }
                    checkInFlowData.InserAsync(entity).GetAwaiter().GetResult();
                    return checkInRecordData.UpdateAsync(updateEntity).GetAwaiter().GetResult().As<AttendanceCheckInRecordDto>();
                default:
                    break;
            }
            return updateEntity.As<AttendanceCheckInRecordDto>();
        }


        private bool IsField(AttendanceGroupViewForStaffAppDto groupView, CheckInMode checkInMode, double longitude, double latitude)
        {
            List<bool> result = new List<bool>();
            if (checkInMode == CheckInMode.Location)
            {
                foreach (var location in groupView.Modes.Locations)
                {
                    double dis = CoordinateUtils.CoordinateDifference(longitude, latitude, location.Longitude, location.Latitude);
                    result.Add(dis > location.EffectiveScope);
                }
            }
            else { result.Add(false); }
            return result.All(s => s);

        }
        /// <summary>
        /// 获取当前员工的打卡状态列表
        /// </summary>
        /// <param name="admin">当前员工</param>
        /// <param name="date">日期</param>
        /// <returns></returns>
        public List<AttendanceCheckInRecordDto> GetCurrentCheckInRecordList(AdminDto admin, DateTime date)
        {
            return checkInRecordData.GetByAsync(admin.AdminId, admin.DepartmentId.Value, date.Date).GetAwaiter().GetResult().As<List<AttendanceCheckInRecordDto>>();
        }

        /// <summary>
        /// 获取当前员工的打卡以及流水列表
        /// </summary>
        /// <param name="admin">当前员工</param>
        /// <param name="date">日期</param>
        /// <returns></returns>
        public List<AttendanceCheckInRecordViewDto> GetCheckInRecordWithFlows(AdminDto admin, DateTime date)
        {
            List<AttendanceCheckInRecordViewDto> list = this.GetCurrentCheckInRecordList(admin, date).As<List<AttendanceCheckInRecordViewDto>>();
            foreach (var recordDto in list.GroupBy(s => new { s.BaseCheckInTime, s.CheckInRecordId }).Select(s => s.First()))
            {
                recordDto.Flows = checkInFlowData.GetListByCheckInRecordIdAsync(recordDto.CheckInRecordId).GetAwaiter().GetResult().As<List<AttendanceCheckInFlowDto>>();

            }
            return list;
        }


        /// <summary>
        /// 分页获取打卡记录信息
        /// </summary>
        /// <param name="adminDto">当前登录管理员</param>
        /// <param name="pageIndex">分页页码</param>
        /// <param name="pageSize">分页页数</param>
        /// <param name="ascending">是否升序</param>
        /// <param name="sourceType">来源类型</param>
        /// <param name="status">打卡状态</param>
        /// <param name="checkInType">打卡方式</param>
        /// <param name="startTime">打卡开始时间</param>
        /// <param name="endTime">打卡结束时间</param>
        /// <param name="departmentId">部门Id</param>
        /// <returns></returns>
        public PageableList<AttendanceCheckInRecordDto> GetList(AdminDto adminDto, int pageIndex, int pageSize, bool? ascending, SourceType? sourceType, CheckInStatus? status, CheckInType? checkInType, DateTime? startTime = null, DateTime? endTime = null, int? departmentId = null)
        {
            var (list, count) = checkInRecordData.GetListAsync(adminDto.CompanyId, adminDto.AdminId, pageIndex, pageSize, ascending, sourceType, status, checkInType, startTime, endTime, departmentId).GetAwaiter().GetResult();
            return new PageableList<AttendanceCheckInRecordDto>
            {
                Count = count,
                Items = list.As<IEnumerable<AttendanceCheckInRecordDto>>(),
                PageIndex = pageIndex,
                PageSize = pageSize
            };
        }
    }
}
